package com.zws.cucumber;

import org.springframework.util.ReflectionUtils;
import org.springframework.test.util.TestSocketUtils;
import sun.misc.Unsafe;

import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.security.SecureRandom;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;

/**
 * author: zws
 */
public class ZwsUtils {

    private static final ConcurrentMap<Integer, Integer> usedAvailablePortMaps = new ConcurrentHashMap<>();

    private static final Unsafe unsafe;

    static {
        Field theUnsafeField = ReflectionUtils.findField(Unsafe.class, "theUnsafe");
        ReflectionUtils.makeAccessible(theUnsafeField);
        unsafe = (Unsafe) ReflectionUtils.getField(theUnsafeField, null);
    }

    public static int nextPort(String name) {
        long s = System.currentTimeMillis();
        int port;
        do {
            port = TestSocketUtils.findAvailableTcpPort();
        } while (usedAvailablePortMaps.put(port, port) != null);
        long e = System.currentTimeMillis();
        System.out.printf("port [%s]: %d, elapsed: %dms\n", name, port, e - s);
        return port;
    }

    public static <T> T getFieldValue(Class<?> clazz, String fieldName, Object object) {
        try {
            Field field = ReflectionUtils.findField(clazz, fieldName);
            field.setAccessible(true);
            return (T) ReflectionUtils.getField(field, object);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    public static void setFieldValue(Class<?> clazz, String fieldName, Object object, Object newValue) {
        try {
            Field field = ReflectionUtils.findField(clazz, fieldName);
            ReflectionUtils.makeAccessible(field);

            if (Modifier.isStatic(field.getModifiers())) {
                setStaticFieldValue(field, newValue);
            } else {
                setObjectFieldValue(field, object, newValue);
            }

        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    private static void setObjectFieldValue(Field field, Object object, Object newValue) throws IllegalAccessException {
        Object oldValue = field.get(object);
        field.set(object, newValue);
        System.out.printf("setNonFinalFieldValue, field: %s, oldValue: %s, newValue: %s\n", field.getName(), oldValue, newValue);
    }

    private static void setStaticFieldValue(Field field, Object newValue) throws IllegalAccessException {
        Object oldValue = field.get(null);

        Object base = unsafe.staticFieldBase(field);
        long offset = unsafe.staticFieldOffset(field);
        if (field.getType().isPrimitive()) {
            Class<?> type = field.getType();
            if (type == boolean.class) {
                unsafe.putBooleanVolatile(base, offset, (boolean) newValue);
            } else if (type == byte.class) {
                unsafe.putByte(base, offset, (byte) newValue);
            } else if (type == char.class) {
                unsafe.putCharVolatile(field, offset, (char) newValue);
            } else if (type == short.class) {
                unsafe.putShortVolatile(field, offset, (short) newValue);
            } else if (type == int.class) {
                unsafe.putIntVolatile(oldValue, offset, (int) newValue);
            } else if (type == long.class) {
                unsafe.putLongVolatile(base, offset, (long) newValue);
            } else if (type == float.class) {
                unsafe.putFloatVolatile(base, offset, (float) newValue);
            } else {
                unsafe.putDoubleVolatile(base, offset, (double) newValue);
            }
        } else {
            unsafe.putObjectVolatile(base, offset, newValue);
        }


        System.out.printf("setStaticFinalFieldValue, field: %s, oldValue: %s, newValue: %s\n", field.getName(), oldValue, newValue);
    }

    private static final byte staticFinalByte;
    private static final int staticFinalInt;
    private static final int staticFinalLong;

    private static byte staticByte;
    private static int staticInt;
    private static int staticLong;

    static {
        SecureRandom r = new SecureRandom();
        staticFinalByte = (byte) r.nextInt(128);
        staticFinalInt = (byte) r.nextInt(1024);
        staticFinalLong = (byte) r.nextInt(2048);

        staticByte = (byte) r.nextInt(128);
        staticInt = (byte) r.nextInt(1024);
        staticLong = (byte) r.nextInt(2048);
    }

    public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException {
        System.out.println(staticFinalByte);
        System.out.println(staticFinalInt);
        System.out.println(staticFinalLong);
        System.out.println(staticByte);
        System.out.println(staticInt);
        System.out.println(staticLong);
        System.out.println("========");
        setFieldValue(ZwsUtils.class, "staticFinalByte", null, (byte) 64);
        setFieldValue(ZwsUtils.class, "staticByte", null, (byte) 64);

        System.out.println(staticFinalByte);
        System.out.println(staticFinalInt);
        System.out.println(staticFinalLong);
        System.out.println(staticByte);
        System.out.println(staticInt);
        System.out.println(staticLong);

    }

}
